<script setup lang="ts">
import { nextTick, onBeforeMount, onMounted, onUnmounted, ref, watch, reactive } from "vue";
import { Delete } from "@element-plus/icons-vue";
import SvgIcon from "../utils/SvgIcon.vue";
import SerialDialog from "./serialDialog.vue";
import { storeToRefs } from "pinia";
import { NumberWord, serialStore } from "../../store"
import { ElMessage } from "element-plus";
import { SerialPort, available_ports, StopBits, Parity, DataBits } from 'tauri-plugin-serial-api'
import { save as saveDialog } from "@tauri-apps/plugin-dialog";
import { invoke } from "@tauri-apps/api/core";


const rows = ref()
const sendMode = ref('ASCII')
const receiveMode = ref('ASCII')
const serialState = ref(false)
const times = ref(10)
const timeShow = ref(false)
const dialog = ref()
const inputValue = ref('')
const chatView = ref([])
const scrollHeight = ref()
const scroll = ref()
const store = serialStore()
const storeRef = storeToRefs(store)
const newLine = ref(true)
const timeSend = ref(false)
const serial = ref({ title: '串口号', value: storeRef.serial, list: storeRef.serialPort })

const configData = reactive({
    baud: {
        title: '波特率',
        value: storeRef.baud,
        list: storeRef.baudRate,
    },
    data: {
        title: '数据位',
        value: storeRef.data,
        list: storeRef.dataBits,
    },
})


let serialport: SerialPort;


const getSerialPorts = async () => {
    const val = await available_ports()
    storeRef.serialPort.value = val
    storeRef.serial.value = val[0]
}

onBeforeMount(async () => {
    await getSerialPorts()
})

let timer


watch([timeSend, times], async () => {
    if (timeSend.value) {
        if (serialport === undefined) {
            ElMessage({
                message: `串口未开启`,
                type: 'error'
            })
            timeSend.value = false
            return
        }
        if (inputValue.value === '') {
            ElMessage({
                message: `请输入发送内容`,
                type: 'warning'
            })
            timeSend.value = false
            return
        }
        if (timer) {
            window.clearInterval(Number(timer))
            timer = null
        }
        timer = window.setInterval(async () => {
            await sendMessage()
        }, 1000 * times.value)
        console.log(timer)
    } else {
        window.clearInterval(Number(timer))
        timer = null
    }
}, { deep: true })

watch(chatView, async () => {
    const num = Math.ceil(parseInt(scrollHeight.value.replace('px', '')) / 21)
    if (num <= chatView.value.length) {
        await nextTick()
        scroll.value.setScrollTop(parseInt(scrollHeight.value.replace('px', '')) + chatView.value.length * 25)
    }
}, { deep: true })

watch(storeRef.baud, async () => {
    if (serialport.isOpen) {
        await serialport.setBaudRate(storeRef.baud.value)
        ElMessage({
            message: `串口波特兰修改成功为: ${storeRef.baud.value}`,
            type: 'success'
        })
    }
}, { deep: true })

watch(storeRef.stop, async () => {
    if (serialport.isOpen) {
        await serialport.setStopBits(StopBits[NumberWord[storeRef.stop.value] as StopBits])
        ElMessage({
            message: `串口停止位修改成功为: ${storeRef.stop.value}`,
            type: 'success'
        })
    }
}, { deep: true })

watch(storeRef.parity, async () => {
    if (serialport.isOpen) {
        await serialport.setParity(storeRef.parity.value as Parity)
        ElMessage({
            message: `串口校验位修改成功为: ${storeRef.parity.value}`,
            type: 'success'
        })
    }
}, { deep: true })

watch(storeRef.data, async () => {
    if (serialport.isOpen) {
        await serialport.setDataBits(DataBits[NumberWord[storeRef.data.value] as DataBits])
        ElMessage({
            message: `串口数据位修改成功为: ${storeRef.data.value}`,
            type: 'success'
        })
    }
}, { deep: true })

watch(receiveMode, () => {
    if (serialport === undefined) return
    if (serialport.isOpen) {
        serialport.setEncode(receiveMode.value === 'ASCII' ? 'utf-8' : 'none')
    }
}, { deep: true }
)

window.addEventListener('resize', () => {
    const element = document.getElementsByClassName('card-send')[0]
    rows.value = Math.floor((element.clientHeight - 88) / 20)
    const height = document.getElementsByClassName('serial-card-body')[0].clientHeight
    scrollHeight.value = `${height - 85}px`
})

onMounted(async () => {
    const element = document.getElementsByClassName('card-send')[0]
    rows.value = Math.floor((element.clientHeight - 88) / 20)
    const height = document.getElementsByClassName('serial-card-body')[0].clientHeight
    scrollHeight.value = `${height - 85}px`
})

onUnmounted(async () => {
    serialport = undefined
})

const openSerial = async () => {
    if (serialState.value) {
        await serialport.stopListening()
        await serialport.close()
        ElMessage({
            message: '串口关闭成功',
            type: 'success'
        })
        serialState.value = false
        serialport = undefined
        timeSend.value = false
        window.clearInterval(Number(timer))
        timer = null
    } else {
        serialport = new SerialPort({
            path: storeRef.serial.value,
            baudRate: storeRef.baud.value,
            dataBits: DataBits[NumberWord[storeRef.stop.value] as DataBits],
            stopBits: StopBits[NumberWord[storeRef.stop.value] as StopBits],
            parity: storeRef.parity.value as Parity,
            encoding: receiveMode.value === 'ASCII' ? 'utf-8' : 'none',
        })
        console.log(serialport.encoding)
        await serialport.closeAll()
        await serialport.open()
        if (serialport.isOpen) {
            serialState.value = true
            ElMessage({
                message: '串口打开成功',
                type: 'success'
            })
            await serialport.startListening()
            await serialport.listen(data => {
                chatView.value.push({
                    time: new Date().toLocaleTimeString(),
                    message: receiveMode.value === 'ASCII' ? data : Array.prototype.map
                        .call(data, (x) => ('00' + x.toString(16)).slice(-2))
                        .join(' '),
                    mode: true
                })
            })
        } else {
            ElMessage({
                message: '串口打开失败',
                type: 'error'
            })
        }
    }
}

const moreSerialConfig = () => {
    dialog.value.openDialog()
}

const sendContent = async() => {
    const content = inputValue.value + (newLine.value ? '\n' : '')
    if (sendMode.value === 'ASCII') {
        await serialport.write(content)
    } else {
        const encoder = new TextEncoder();
        const data = encoder.encode(content.toString());
        await serialport.writeBinary(data)
    }
    chatView.value.push({
        time: new Date().toLocaleTimeString(),
        message: inputValue.value,
        mode: false
    })
}

const sendMessage = async () => {
    if (!serialState.value) {
        ElMessage({
            message: '串口未打开，请开启串口',
            type: 'warning'
        })
    }
    await sendContent()
}

const checkChange = (value: any) => {
    timeShow.value = value
}

const clearReceive = () => {
    chatView.value = []
}

const saveReceive = async () => {
    console.log('Save')
    const filepath = await saveDialog({
        title: '保存接收区',
        filters: [{ name: '文本文件', extensions: ['txt'] }],
    })
    if (filepath === null) {
        ElMessage({
            message: '文件保存失败',
            type: 'error'
        })
        return
    }
    const chatList = []
    chatView.value.forEach(item => {
        chatList.push(item.time + (item.mode ? '接收: ' : '发送: ') + item.message)
    })
    await invoke('write_file', { filepath: filepath, content: chatList.join('\n') })
    ElMessage({
        message: '保存成功',
        type: 'success'
    })
}

</script>

<template>
    <div style="height: 100%">
        <el-card class="serial-card" body-class="serial-card-body" style="height: 48%; position: relative"
            body-style="height: 100%; padding: 10px">
            <div style="margin-bottom: 10px; display: flex;
            justify-content: space-between;">
                <div>
                    <el-checkbox label="添加时间戳" @change="checkChange" />
                    <el-divider direction="vertical" />
                    <el-radio-group v-model="receiveMode">
                        <el-radio value="ASCII" size="small">ASCII</el-radio>
                        <el-radio value="HEX" size="small">HEX</el-radio>
                    </el-radio-group>
                </div>
                <div>
                    <el-tooltip effect="dark" content="清空接收区" placement="top-start">
                        <el-button type="success" circle @click="clearReceive">
                            <el-icon>
                                <Delete />
                            </el-icon>
                        </el-button>
                    </el-tooltip>
                    <el-tooltip effect="dark" content="保存接收区" placement="top-start">
                        <el-button type="warning" circle @click="saveReceive">
                            <el-icon>
                                <svg-icon name="save" />
                            </el-icon>
                        </el-button>
                    </el-tooltip>
                </div>
            </div>
            <el-card body-class="card-scroll" style="height: calc(100% - 60px)" body-style="height: 100%; padding: 5px">
                <el-scrollbar class="scroll" :height="scrollHeight" always ref="scroll">
                    <div v-for="item in chatView">
                        <div v-if="item.mode" style="text-align: start">
                            <el-text type="success" v-if="timeShow">{{ item.time }}: {{ item.message }}</el-text>
                            <el-text type="success" v-else>{{ item.message }}</el-text>
                        </div>
                        <div v-else style="text-align: end; margin: 2px 10px">
                            <el-text type="primary" v-if="timeShow">{{ item.time }}: {{ item.message }}</el-text>
                            <el-text type="primary" v-else>{{ item.message }}</el-text>
                        </div>
                    </div>
                </el-scrollbar>
            </el-card>
        </el-card>

        <el-card class="serial-card">
            <!--串口配置区域-->
            <span class="config-item">
                <el-text size="default">{{ serial.title }}: </el-text>
                <el-select v-model="serial.value" :disabled="serialState" :placeholder="serial.title" size="default"
                    style="width: 100px">
                    <el-option v-for="it in serial.list" :key="it" :label="it" :value="it" />
                </el-select>
            </span>
            <span class="config-item" v-for="item in configData">
                <el-text size="default">{{ item.title }}: </el-text>
                <el-select v-model="item.value" :placeholder="item.title" size="default" style="width: 100px">
                    <el-option v-for="it in item.list" :key="it" :label="it" :value="it" />
                </el-select>
            </span>
            <el-button type="success" plain @click="moreSerialConfig">更多串口配置</el-button>
            <el-button type="success" plain @click="openSerial">
                <template v-if="serialState">关闭串口</template>
                <template v-else>开启串口</template>
            </el-button>
            <serial-dialog ref="dialog"></serial-dialog>
        </el-card>
        <el-card class="card-send" style="height: calc(52% - 77px)">
            <!--     串口发送数据区域       -->
            <div style="margin-bottom: 10px">
                <el-radio-group v-model="sendMode">
                    <el-radio value="ASCII" size="small">ASCII</el-radio>
                    <el-radio value="HEX" size="small">HEX</el-radio>
                </el-radio-group>
                <el-checkbox v-model="newLine" style="margin-left: 10px;">自动换行</el-checkbox>
                <el-divider direction="vertical" />
                <span style="margin-left: 10px">
                    <el-checkbox v-model="timeSend" style="margin-left: 10px;">定时发送: </el-checkbox>
                    <el-input-number v-model="times" :disabled="!timeSend" controls-position="right"
                        style="width: 100px; margin-left: 10px">
                        <template #suffix>S</template>
                    </el-input-number>
                </span>

            </div>
            <el-row :gutter="3">
                <el-col :span="22">
                    <el-input v-model="inputValue" type="textarea" :rows="rows" :resize="'none'"></el-input>
                </el-col>
                <el-col :span="2" style="position: relative;">
                    <el-button class="send-bnt" style="top: 0" @click="inputValue = ''">清空</el-button>
                    <el-button class="send-bnt" style="bottom: 0" @click="sendMessage">发送</el-button>
                </el-col>
            </el-row>
        </el-card>
    </div>
</template>

<style scoped>
.serial-card {
    margin-bottom: 5px;
}

.send-bnt {
    width: 100%;
    margin: 0 5px;
    position: absolute;
}

.config-item {
    margin-right: 10px;
}
</style>